package com.weixin.service;

import com.main.HttpClientExecutor;
import com.alipay.util.UtilDate;
import com.main.*;
import com.weixin.WeixinRequest;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.sql.*;
import java.util.*;
import java.util.Date;

/**
 * 微信支付的Service服务
 */
public class WeixinService {
    private static Logger logger = Logger.getLogger(WeixinService.class.getName());


    //获取sign
    private static String getSign(Map<String, String> xmlMap, String secretkey) {
        List<String> keys = new ArrayList<String>(xmlMap.keySet());
        Collections.sort(keys);
        String string = "";
        for (String key : keys) {
            if (!"sign".equals(key))
                string += (key + "=" + xmlMap.get(key) + "&");
        }
        string += ("key=" + secretkey);
        logger.info("加密前："+string);
        String md5 = MD5Util.MD5(string);
        logger.info("加密后："+md5);
        return md5;
    }
    /**通过微信返回的map生成LogMap*/
    private static ExtendMap<String,Object> getLogMap(Map<String, String> weixinResult,String retCode,String retMsg){
        ExtendMap<String,Object> map = new ExtendMap<>();
        //一个一个获取出来
        String orderId = weixinResult.get("out_trade_no");
        String orderName = "微信支付：订单号："+orderId;
        BigDecimal orderSum = new BigDecimal(weixinResult.get("total_fee")).divide(new BigDecimal(100),2,RoundingMode.HALF_UP);
        int payType = OrderPayTypeEnums.WEIXINPAY.getValue();
        int payState = "SUCCESS".equals(retCode)?1:2;
        String notifyTime = UtilDate.getNowDate();

        map.put("order_id",orderId);
        map.put("order_name",orderName);
        map.put("order_sum",orderSum);
        map.put("pay_type",payType);
        map.put("pay_state",payState);
        map.put("notify_time",notifyTime);
        map.put("remarks", retMsg);
        return map;
    }
    /**生成微信订单记录*/
    private static ExtendMap<String,Object> getAddOrderLogMap(String orderId ,int totalFee,String thirdPartOrderId){
        ExtendMap<String,Object> map = new ExtendMap<>();
        map.put("order_id",orderId);
        map.put("order_name","微信支付：订单号："+orderId);
        map.put("order_sum",new BigDecimal(totalFee).divide(new BigDecimal(100),2,RoundingMode.HALF_UP));
        map.put("third_order_no",thirdPartOrderId);
        map.put("pay_type",OrderPayTypeEnums.WEIXINPAY.getValue());
        map.put("pay_state",0);
        map.put("notify_time","");
        map.put("remarks", "下单成功");
        return map;
    }

    //微信统一下单
    //参数：
    // title 标题
    // body 商品内容
    // orderId 订单编号
    // num 数量
    // price 价格
    //
    public static void weixinAppOrder(ExtendMap<String, Object> paramMap, String url, String ip,HttpServletResponse response) {
        try {
            BaseResponse retResponse ;
            ServletOutputStream outputStream = response.getOutputStream();  //输出流
            if (paramMap == null || paramMap.size() == 0) {
                retResponse = new BaseResponse(BaseResponse.FAIL,"参数错误",null);
            }else{

                String appType = url.substring(url.lastIndexOf("weixinAppOrder/") + "weixinAppOrder/".length(), url.lastIndexOf("."));   //app类型
                ParseConfig.execute();
                logger.info(ParseConfig.config);
                //初始化参数
                String appid = ParseConfig.getConfigMap("weixin." + appType).get("appid");
                String mch_id = ParseConfig.getConfigMap("weixin." + appType).get("mchId"); //商户号
                String appName = ParseConfig.getConfigMap("weixin." + appType).get("appName");//app名称
                String nonce_str = StringUtil.getRandomString(16);
                String key = ParseConfig.getConfigMap("weixin." + appType).get("key"); //秘钥
                String webSite = ParseConfig.getConfigMap("notify").get("payWebSite");  //网站
                String title = paramMap.getString("title");
                String body = paramMap.getString("body");
                String orderId = paramMap.getString("orderId");
                Integer num = paramMap.getInt("num");
                if (num == 0) num = 1;
                Float priceFloat = paramMap.getFloat("price");
                Integer price = priceFloat.intValue();
                logger.info("------------------- price = "+price+" -------------------");
                logger.info("-----------------查看数据库是否有订单支付记录--------------");
                String thirdPartOrderId = null;
                try {
                    thirdPartOrderId = getThirdPartOrderNo(orderId);
                } catch (Exception e) {
                    //订单已经支付
                    retResponse= new BaseResponse(BaseResponse.FAIL,e.getMessage(), null);
                    response.setContentType("application/json;charset=UTF-8");
                    outputStream.write(retResponse.toJSON().getBytes("UTF-8"));
                    outputStream.flush();
                    outputStream.close();
                    logger.info(retResponse.getMsg());
                    return;
                }

                if(StringUtils.isBlank(thirdPartOrderId)){
                    //如果第三方支付订单为空，则表示没有支付过
                    //商品信息
                    String detail =
                            "{\"goods_detail\":" +
                                    "[{\"goods_id\":\"" + orderId + "\"" +
                                    ",\"goods_name\":\"" + title + "\" " +
                                    ",\"quantity\":" + num + "" +
                                    ",\"price\":" + price + "" +
                                    (StringUtil.isBlank(body) ? "" : ",\"body\":\""+body+"\"") +
                                    "}]" +
                                    "}";
                    //参数放入map
                    Map<String, String> xmlMap = new LinkedHashMap<>();
                    xmlMap.put("appid", appid);
                    xmlMap.put("attach", appType);
                    xmlMap.put("body", appName + "-" + title);
                    xmlMap.put("detail", detail);
                    xmlMap.put("mch_id", mch_id);
                    xmlMap.put("nonce_str", nonce_str);
                    xmlMap.put("notify_url", webSite + "/weixinYbNotify.do");
                    xmlMap.put("out_trade_no", orderId);
                    xmlMap.put("spbill_create_ip", ip);
                    xmlMap.put("total_fee", num * price + "");
                    xmlMap.put("trade_type", "APP");
                    //获得签名
                    String sign = getSign(xmlMap, key);     //签名
                    xmlMap.put("sign", orderId);
                    String xml =
                            "<xml>" +
                                    "<appid>" + appid + "</appid>" +
                                    "<attach>" + appType + "</attach>" +
                                    "<body>" + appName + "-" + title + "</body>" +
                                    "<detail>" + detail + "</detail>" +
                                    "<mch_id>" + mch_id + "</mch_id>" +
                                    "<nonce_str>" + nonce_str + "</nonce_str>" +
                                    "<notify_url>" + webSite + "/weixinYbNotify.do" + "</notify_url>" +
                                    "<out_trade_no>" + orderId + "</out_trade_no>" +
                                    "<spbill_create_ip>" + ip + "</spbill_create_ip>" +
                                    "<total_fee>" + (num * price) + "</total_fee>" +
                                    "<trade_type>APP</trade_type>" +
                                    "<sign>" + sign + "</sign>" +
                                    "</xml>";
                    logger.info(xml);
                    String result = null;
                    //发起请求
                    try {
                        result = WeixinRequest.httpsRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", xml);
                    } catch (IOException | NoSuchAlgorithmException | NoSuchProviderException  | KeyManagementException e) {
                        e.printStackTrace();
                    }

                    logger.info(result);
                    Map<String, String> weixinResult = XmlParseUtil.toMap(result);
                    logger.info(weixinResult);
                    //验证结果
                    if (weixinResult == null || weixinResult.size() == 0) {
                        retResponse= new BaseResponse(BaseResponse.FAIL, "返回为空", null);
                    } else if ("SUCCESS".equals(weixinResult.get("result_code"))) {
                        weixinResult.put("timestamp",""+(new Date().getTime()/1000));
                        retResponse= new BaseResponse(BaseResponse.SUCCESS, "下单成功", weixinResult);
                        //下单成功需要写日志
                        ExtendMap<String, Object> addOrderLogMap = getAddOrderLogMap(orderId, num * price, weixinResult.get("prepay_id"));
                        writeLog(addOrderLogMap);

                    } else
                        retResponse= new BaseResponse(BaseResponse.FAIL, "微信支付下单失败", null);
                }else {
                    //如果以及有订单，则查询微信订单并返回
                    Map<String ,String > weixinResult = new HashMap<>();
                    weixinResult.put("return_code","SUCCESS");
                    weixinResult.put("return_msg","OK");
                    weixinResult.put("appid",appid);
                    weixinResult.put("mch_id",mch_id);
                    weixinResult.put("nonce_str",nonce_str);
                    weixinResult.put("result_code","SUCCESS");
                    weixinResult.put("prepay_id",thirdPartOrderId);
                    weixinResult.put("trade_type","APP");
                    String sign = getSign(weixinResult,key);
                    weixinResult.put("sign",sign);
                    weixinResult.put("timestamp",""+(new Date().getTime()/1000));
                    retResponse= new BaseResponse(BaseResponse.SUCCESS, "下单成功", weixinResult);
                }
            }

            response.setContentType("application/json;charset=UTF-8");
            outputStream.write(retResponse.toJSON().getBytes("UTF-8"));
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //微信异步通知
    public static void weixinYbNotify(HttpServletRequest request, HttpServletResponse response) {
        try {
            ServletInputStream inputStream = request.getInputStream();  //输入流
            ServletOutputStream outputStream = response.getOutputStream();  //输出流
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); //输入流Reader
            String result = "";
            String tmp = "";
            while ((tmp = bufferedReader.readLine()) != null) {
                result += tmp;
            } //获取参数
            inputStream.close();
            bufferedReader.close();
            //转换成Map
            Map<String, String> weixinResult = XmlParseUtil.toMap(result);
            logger.info("微信支付回调参数：" + weixinResult);
            String retMsg = "";
            String retCode = "FAIL";
            if (weixinResult != null && weixinResult.size() != 0) {
                String appType = weixinResult.get("attach");
                String key = ParseConfig.getConfigMap("weixin." + appType).get("key"); //秘钥
                String signCome = weixinResult.get("sign");
                String sign = getSign(weixinResult, key);
                if (!"SUCCESS".equals(weixinResult.get("result_code"))) {
                    //微信返回失败信息
                    retMsg = weixinResult.get("return_msg");
                } else if (StringUtil.equals(signCome, sign)) {
                    //支付成功
                    retMsg = "支付成功";
                    retCode = "SUCCESS";
                } else {
                    //失败
                    retMsg = "本地签名验证错误";
                }
            } else {
                retMsg = "参数错误";
            }
            //响应报文体拼接
            String pushXml =
                    "<xml>" +
                            "<return_code><![CDATA[" + retCode + "]]></return_code>" +
                            "<return_msg><![CDATA[" + retMsg + "]]></return_msg>" +
                            "</xml>";
            //返回给微信
            response.setContentType("text/xml");
            outputStream.write(pushXml.getBytes("UTF-8"));
            outputStream.flush();
            outputStream.close();

            //写日志并发起网站的回调
            ExtendMap<String, Object> logMap = getLogMap(weixinResult, retCode, retMsg);
            logger.info("微信支付写日志Map："+logMap);
            if(1 == updateLog(logMap)){
                ExtendMap<String,String> notify=ParseConfig.getConfigMap("notify");
                String yburl=notify.getString("yburl");
                yburl = yburl.indexOf("?")>0 ? yburl+"&orderId="+logMap.get("order_id") : yburl+"?orderId="+logMap.get("order_id");
                logger.info(yburl);
                HttpClientExecutor.execute(yburl);
            }
            logger.info("微信支付回调返回结果：" + pushXml);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public static void main(String[] args) {
        // title 标题
        // body 商品内容
        // orderId 订单编号
        // num 数量
        // price 价格
        ExtendMap<String, Object> paramMap = new ExtendMap<>();
        paramMap.put("title", "这是一个测试商品");
        paramMap.put("body", "这是一个测试商品");
        paramMap.put("orderId", "TEST001");
        paramMap.put("num", "1");
        paramMap.put("price", "1");
//        weixinAppOrder(paramMap, "/weixinAppOrder/company.do", "14.152.90.121");
//{
// transaction_id=4008272001201608232017366871,
// nonce_str=ydj8rtkkn7jgvepy,
// bank_type=CFT,
// openid=oekZMwIDSfCvz4gGq39Aw37yvS24,
// sign=083D476DFE3321805AFC1C8685B90613,
// fee_type=CNY,
// mch_id=1321572601,
// cash_fee=1,
// out_trade_no=RC1471920574481002,
// appid=wx9e9a27fe1127d0f2,
// total_fee=1,
// trade_type=APP,
// result_code=SUCCESS,
// attach=user,
// time_end=20160823105027,
// is_subscribe=N,
// return_code=SUCCESS}
        ExtendMap<String,String> notifyMap = new ExtendMap<>();
        notifyMap.put("transaction_id","4008272001201608232017366871");
        notifyMap.put("nonce_str","ydj8rtkkn7jgvepy");
        notifyMap.put("bank_type","CFT");
        notifyMap.put("openid","oekZMwIDSfCvz4gGq39Aw37yvS24");
        notifyMap.put("sign","083D476DFE3321805AFC1C8685B90613");
        notifyMap.put("fee_type","CNY");
        notifyMap.put("mch_id","1321572601");
        notifyMap.put("cash_fee","1");
        notifyMap.put("out_trade_no","RC1471920574481002");
        notifyMap.put("appid","wx9e9a27fe1127d0f2");
        notifyMap.put("total_fee","1");
        notifyMap.put("trade_type","APP");
        notifyMap.put("result_code","SUCCESS");
        notifyMap.put("attach","user");
        notifyMap.put("time_end","20160823105027");
        notifyMap.put("is_subscribe","N");
        notifyMap.put("return_code","SUCCESS");

        String key = ParseConfig.getConfigMap("weixin." + "user").get("key"); //秘钥
        String signCome = notifyMap.get("sign");
        String sign = getSign(notifyMap, key);
        System.out.println(signCome);
        System.out.println(sign);
    }

    /**输出订单支付结果到数据库*/
    public static int writeLog(ExtendMap<String,Object> logMap){
        int result=0;
        ExtendMap<String,String> database=ParseConfig.getConfigMap("database");
        Connection conn=null;
        try{
            Class.forName(database.getString("driverClassName"));
            conn= DriverManager.getConnection(database.getString("url"),database.getString("username"),database.getString("password"));

            PreparedStatement ps=conn.prepareStatement("select pay_state from t_pay_log where order_id=?");
            ps.setObject(1,logMap.get("order_id"));
            ResultSet rs=ps.executeQuery();
            if(rs.next()){
                logMap.put("pay_state",rs.getObject("pay_state"));
            }else{
                ps=conn.prepareStatement("insert into t_pay_log(order_id,order_name,order_sum,pay_type,pay_state,notify_time,remarks,third_order_no) values(?,?,?,?,?,?,?,?)");
                ps.setObject(1,logMap.get("order_id"));
                ps.setObject(2,logMap.get("order_name"));
                ps.setObject(3,logMap.get("order_sum"));
                ps.setObject(4,logMap.get("pay_type"));
                ps.setObject(5,logMap.get("pay_state"));
                ps.setObject(6,logMap.get("notify_time"));
                ps.setObject(7,logMap.get("remarks"));
                ps.setObject(8,logMap.get("third_order_no"));
                ps.executeUpdate();
                result=1;
            }
        }catch(Exception e){
            result=1;
            logger.info(logMap);
//			e.printStackTrace();
//			logger.error(e.getMessage(),e);
        }finally{
            try{conn.close();}catch(Exception e){}
        }
        return result;
    }

    /**获取数据空中有的thirdPartOrderNo， 如果没有就返回null  如果有并且已经支付，则抛异常 */
    public static String getThirdPartOrderNo(String order_id) throws Exception{
        String thirdPartOrderNo = null;
        ExtendMap<String,String> database=ParseConfig.getConfigMap("database");
        Connection conn=null;
        int pay_state = 0;
        try {
            Class.forName(database.getString("driverClassName"));
            conn= DriverManager.getConnection(database.getString("url"),database.getString("username"),database.getString("password"));
            PreparedStatement ps=
                    conn.prepareStatement("select third_order_no,pay_state from t_pay_log where order_id=?");
            ps.setObject(1,order_id);
            ResultSet rs=ps.executeQuery();
            if(rs.next()){
                thirdPartOrderNo = rs.getString("third_order_no");
                pay_state = rs.getInt("pay_state");
            }
        } catch (Exception e) {
            thirdPartOrderNo = null;
            logger.info(order_id);
            e.printStackTrace();
        } finally {
            try{conn.close();}catch(Exception e){}
        }
        if(pay_state != 0)
            throw new Exception("订单已经支付，无需再支付订单");
        return thirdPartOrderNo;
    }

    /**输出订单支付结果到数据库*/
    public static int updateLog(ExtendMap<String,Object> logMap){
        int result=0;
        ExtendMap<String,String> database=ParseConfig.getConfigMap("database");
        Connection conn=null;
        try{
            Class.forName(database.getString("driverClassName"));
            conn= DriverManager.getConnection(database.getString("url"),database.getString("username"),database.getString("password"));

            PreparedStatement ps=conn.prepareStatement("select pay_state from t_pay_log where order_id=?");
            ps.setObject(1,logMap.get("order_id"));
            ResultSet rs=ps.executeQuery();
            if(rs.next()){
                ps=conn.prepareStatement("update t_pay_log set pay_state = ? ,notify_time = ? where order_id = ?");
                ps.setObject(1,logMap.get("pay_state"));
                ps.setObject(2,logMap.get("notify_time"));
                ps.setObject(3,logMap.get("order_id"));
                ps.executeUpdate();
                result=1;
            }else{
                ps=conn.prepareStatement("insert into t_pay_log(order_id,order_name,order_sum,pay_type,pay_state,notify_time,remarks,third_order_no) values(?,?,?,?,?,?,?,?)");
                ps.setObject(1,logMap.get("order_id"));
                ps.setObject(2,logMap.get("order_name"));
                ps.setObject(3,logMap.get("order_sum"));
                ps.setObject(4,logMap.get("pay_type"));
                ps.setObject(5,logMap.get("pay_state"));
                ps.setObject(6,logMap.get("notify_time"));
                ps.setObject(7,logMap.get("remarks"));
                ps.setObject(8,logMap.get("third_order_no"));
                ps.executeUpdate();
                result=1;
            }
        }catch(Exception e){
            result=1;
            logger.info(logMap);
//			e.printStackTrace();
//			logger.error(e.getMessage(),e);
        }finally{
            try{conn.close();}catch(Exception e){}
        }
        return result;
    }

}
